package cn.mw.cmdb.link.strategy.handler;

import cn.mw.cmdb.entity.InstanceInfo;
import cn.mw.cmdb.entity.InstanceInfoExtend;
import cn.mw.cmdb.enums.MonitorModeType;
import cn.mw.cmdb.link.entity.LinkHistoryTrendDto;
import cn.mw.cmdb.link.entity.MwLinkTargetIpInfo;
import cn.mw.cmdb.link.entity.MwNetWorkLinkDto;
import cn.mw.cmdb.link.enums.LinkDetectionWayEnum;
import cn.mw.cmdb.link.enums.LinkHistoryTypeEnum;
import cn.mw.cmdb.link.param.LinkParam;
import cn.mw.cmdb.link.strategy.LinkDetectionWayHandler;
import cn.mw.cmdb.param.BatchAddAndUpdInstanceParam;
import cn.mw.cmdb.param.FunctionModuleEnum;
import cn.mw.cmdb.param.zabbixResult.ItemApplication;
import cn.mw.cmdb.service.ServiceAction;
import cn.mw.cmdb.service.impl.InstanceServiceImpl;
import cn.mw.cmdb.util.BaseUtil;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.common.MwEngineManageService;
import cn.mw.microMonitorCommon.entity.UnitResult;
import cn.mw.microMonitorCommon.util.IDModelType;
import cn.mw.microMonitorCommon.util.ModuleIDManager;
import cn.mw.microMonitorCommon.util.UnitUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static cn.mw.cmdb.entity.InstanceInfo.SearchPrefix;
import static cn.mw.cmdb.processor.MonitorValue.*;
import static cn.mw.cmdb.processor.PropertyValueManage.Prefix;
import static cn.mw.cmdb.service.impl.InstanceViewImpl.mwInstanceNameKey;
import static cn.mw.components.mongodb.entity.MongoPropertyType.strValueConvert;

/**
 * @author gengjb
 * @description ICMP方式处理
 * @date 2024/8/20 11:32
 */
@Service
@Slf4j
public class LinkICMPWayHandler extends LinkDetectionWayHandler {

    private final List<String> ICMP_ITEM_NAMES = Arrays.asList("ICMP_PING", "ICMP_RESPONSE_TIME", "ICMP_LOSS");

    private final String MODEL_NAME = "modelName";
    private final String ICMP_PING = "ICMP_PING";

    private final String ICMP_GROUP_ID = "69972420710105088";
    private final String ICMP_MODEL_ID = "97211217252188160";
    private final String ICMP_BRAND = "69922946281373696";
    private final String ICMP_SPECIFICATION = "77388321533198336";

    private final String MW_RELATION_LINK = "mw_relationLink";
    private final String ICMP_PORT = "10050";

    private final String DEFAULT_ENGINE = "localhost";

    @DubboReference(check = false, timeout = 12000, mock = "true")
    private MwEngineManageService engineManageService;

    @Autowired
    private ModuleIDManager moduleIDManager;


    @Override
    public LinkDetectionWayEnum handlerType() {
        return LinkDetectionWayEnum.ICMP;
    }

    @Override
    public void getLinkMonitorInfo(List<MwNetWorkLinkDto> dtos) throws Exception {
        //查询ICMP线路的状态，响应时间，丢包率
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(dtos, MODEL_NAME, null);
        if (CollectionUtils.isEmpty(assetInfo)) {
            return;
        }
        Map<Integer, List<String>> serverGroup = getHostIdGroup(assetInfo);
        //查询zabbix数据
        List<ItemApplication> itemApplications = getLinkZabbixMonitorInfo(serverGroup, ICMP_ITEM_NAMES);
        if (CollectionUtils.isEmpty(itemApplications)) {
            return;
        }
        Map<String, List<ItemApplication>> itemMap = itemApplications.stream().collect(Collectors.groupingBy(item -> item.getHostid() + item.getServerId()));
        for (Map.Entry<String, List<ItemApplication>> entry : itemMap.entrySet()) {
            String key = entry.getKey();
            List<InstanceInfoExtend> anExtends = assetInfo.stream().filter(item -> (item.getMonitorTypeId() + item.getMonitorServerId()).equals(key)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(anExtends)) {
                continue;
            }
            String inBandIp = anExtends.get(0).getInBandIp();
            List<MwNetWorkLinkDto> linkDtos = dtos.stream().filter(item -> item.getDetectionTargetIp().equals(inBandIp)).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(linkDtos)) {
                continue;
            }
            for (MwNetWorkLinkDto linkDto : linkDtos) {
                setIcmpValueInfo(linkDto, entry.getValue());
            }
        }
        getLinkBandWidthUtilization(dtos);
    }

    @Override
    public List<MwLinkTargetIpInfo> getTargetIpInfo(LinkParam linkParam) throws Exception {
        List<MwLinkTargetIpInfo> targetIpInfos = new ArrayList<>();
        //获取ICMP类型的目标IP
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, MODEL_NAME, null);
        if (CollectionUtils.isEmpty(assetInfo)) {
            return targetIpInfos;
        }
        for (InstanceInfoExtend instanceInfoExtend : assetInfo) {
            MwLinkTargetIpInfo targetIpInfo = new MwLinkTargetIpInfo();
            targetIpInfo.setInstanceName(instanceInfoExtend.getInstanceName());
            targetIpInfo.setInstanceId(instanceInfoExtend.getId());
            targetIpInfo.setTargetIpAddress(instanceInfoExtend.getInBandIp());
            targetIpInfos.add(targetIpInfo);
        }
        return targetIpInfos;
    }

    @Override
    public void handlerLinkData(MwNetWorkLinkDto netWorkLinkDto) throws Exception {
        //ICMP检测方式处理
        //判断目标资产是否存在
        String detectionTargetIpId = netWorkLinkDto.getDetectionTargetIpId();
        if (StringUtils.isNotEmpty(detectionTargetIpId)) {
            //目标资产存在
            List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, null, Arrays.asList(detectionTargetIpId));
            if (CollectionUtils.isNotEmpty(assetInfo)) {
                InstanceInfoExtend instanceInfoExtend = assetInfo.get(0);
                Query query = new Query(CriteriaWrapper.where(InstanceInfo::getId).is(instanceInfoExtend.getId()));
                Update update = new Update();
                update.set(SearchPrefix + "." + MW_RELATION_LINK, true);
                if (StringUtils.isNotEmpty(netWorkLinkDto.getPollingEngine()) && !netWorkLinkDto.getPollingEngine().equals(DEFAULT_ENGINE)) {
                    update.set(SearchPrefix + "." +ProxyTypeKey, netWorkLinkDto.getProxyType());
                    update.set(SearchPrefix + "." +Prefix + CMDBTool.getFieldName(InstanceInfo::getPollingEngine), netWorkLinkDto.getPollingEngine());
                }else{
                    update.set(SearchPrefix + "." +ProxyTypeKey, netWorkLinkDto.getProxyType());
                    update.set(SearchPrefix + "." +Prefix + CMDBTool.getFieldName(InstanceInfo::getPollingEngine), "");
                }
                InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
                instanceService.updateInstanceCustom(query,update);
            }
            createZabbixLink(LinkDetectionWayEnum.ICMP, detectionTargetIpId);
            return;
        }
        //资产不存在，需要创建ICMP的资产
        String assetsId = createICMPAssets(netWorkLinkDto);
        //获取新增资产的ID添加线路
        createZabbixLink(LinkDetectionWayEnum.ICMP, assetsId);
    }

    /**
     * 创建ICMP资产
     *
     * @param netWorkLinkDto
     */
    private String createICMPAssets(MwNetWorkLinkDto netWorkLinkDto) throws Exception {
        Map<String, Object> instanceParams = new HashMap<>();
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getUsers), netWorkLinkDto.getUsers());
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getOrgIds), netWorkLinkDto.getOrgIds());
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getOrgs), netWorkLinkDto.getOrgs());
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getGroups), netWorkLinkDto.getGroups());
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getGroupId), ICMP_GROUP_ID);
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getModelId), ICMP_MODEL_ID);
        instanceParams.put(mwInstanceNameKey, LinkDetectionWayEnum.ICMP.getType() + "_" + netWorkLinkDto.getDetectionTargetIp());
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getInBandIp), netWorkLinkDto.getDetectionTargetIp());
        instanceParams.put(RuleTypeKey, MonitorModeType.ICMP.getName());
        instanceParams.put(TemplateNameKey, LinkDetectionWayEnum.ICMP.getTemplateName());
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getInterfacesType), 0);
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getMonitorMode), 4);
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getFunctionModule), Arrays.asList(FunctionModuleEnum.enableMonitor.getCode()));
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getPort), ICMP_PORT);
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getBrand), ICMP_BRAND);
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getSpecification), ICMP_SPECIFICATION);
        instanceParams.put(ZabbixStatusKey, true);
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getCheckNowFlag), false);
        instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getMonitorServerId), netWorkLinkDto.getMonitorServerId());
        instanceParams.put(MW_RELATION_LINK, true);
        if (StringUtils.isNotEmpty(netWorkLinkDto.getPollingEngine()) && !netWorkLinkDto.getPollingEngine().equals(DEFAULT_ENGINE)) {
            instanceParams.put(ProxyTypeKey, netWorkLinkDto.getProxyType());
            instanceParams.put(Prefix + CMDBTool.getFieldName(InstanceInfo::getPollingEngine), netWorkLinkDto.getPollingEngine());
        }
        String id = strValueConvert(moduleIDManager.getID(IDModelType.Model));
        instanceParams.put(CMDBTool.getFieldName(InstanceInfo::getId), id);
        BatchAddAndUpdInstanceParam batchAddAndUpdInstanceParam = new BatchAddAndUpdInstanceParam();
        batchAddAndUpdInstanceParam.setParams(Arrays.asList(instanceParams));
        cmdbServiceManage.service(batchAddAndUpdInstanceParam, ServiceAction.batchAdd);
        netWorkLinkDto.setDetectionTargetIpId(id);
        return id;
    }

    @Override
    public List<LinkHistoryTrendDto> getLinkHistoryTrend(MwNetWorkLinkDto netWorkLinkDto, LinkParam linkParam) throws Exception {
        List<LinkHistoryTrendDto> historyTrendDtos = new ArrayList<>();
        LinkHistoryTypeEnum historyTypeEnum = LinkHistoryTypeEnum.getHistoryTypeEnum(linkParam.getQueryType());
        InstanceInfoExtend targetAsset = getTargetAsset(netWorkLinkDto.getDetectionTargetIpId());
        List<Long> times = BaseUtil.getTimeIntervalByType(linkParam.getStartTime(), linkParam.getEndTime(), linkParam.getDateType());
        switch (historyTypeEnum) {
            case FLOW:
                historyTrendDtos = getHistoryTrendData(netWorkLinkDto, linkParam);
                break;
            case DELAY:
                if (targetAsset == null) {
                    break;
                }
                historyTrendDtos = handlerHistoryTrendData(targetAsset.getMonitorTypeId(), targetAsset.getMonitorServerId(), Arrays.asList(ICMP_RESPONSE_TIME), times, linkParam.getHistoryDataType(), linkParam.getValueType());
                break;
            case LOSS_RATE:
                if (targetAsset == null) {
                    break;
                }
                historyTrendDtos = handlerHistoryTrendData(targetAsset.getMonitorTypeId(), targetAsset.getMonitorServerId(), Arrays.asList(ICMP_LOSS), times, linkParam.getHistoryDataType(), linkParam.getValueType());
                break;

        }
        return historyTrendDtos;
    }

    private InstanceInfoExtend getTargetAsset(String instanceId) throws Exception {
        List<InstanceInfoExtend> assetInfo = getTargetDeviceAssetInfo(null, null, Arrays.asList(instanceId));
        if (CollectionUtils.isEmpty(assetInfo)) {
            return null;
        }
        return assetInfo.get(0);
    }

    /**
     * 创建ICMP类型的资产
     */
    private void createICMPAsset() {

    }

    private void setIcmpValueInfo(MwNetWorkLinkDto linkDto, List<ItemApplication> itemApplications) {
        for (ItemApplication itemApplication : itemApplications) {
            String name = itemApplication.getName();
            String lastvalue = itemApplication.getLastvalue();
            if (name.contains(ICMP_PING)) {//设置状态
                linkDto.setStatus(Double.parseDouble(lastvalue) == 1 ? NORMAL : ABNORMAL);
            }
            if (name.contains(ICMP_LOSS)) {//设置丢包率
                linkDto.setLossRate(new BigDecimal(lastvalue).setScale(2, BigDecimal.ROUND_HALF_UP).toString() + itemApplication.getUnits());
            }
            if (name.contains(ICMP_RESPONSE_TIME)) {//设置响应时间
                //单位换算成Ms
                UnitResult unitResult = UnitUtil.getValueMap(lastvalue, MS, itemApplication.getUnits());
                if (unitResult == null) {
                    linkDto.setResponseTime(new BigDecimal(lastvalue).setScale(2, BigDecimal.ROUND_HALF_UP).toString() + itemApplication.getUnits());
                } else {
                    linkDto.setResponseTime(unitResult.getValue() + unitResult.getUnits());
                }
            }
        }
    }
}
